﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.ComponentModel;
using Cinchoo.Core.ETL;
using Cinchoo.Core;

namespace ChoWorkflowServer.Model
{
    public class ChoTaskGroupCategoryComparer : IComparer<ChoTaskGroupCategory>
    {
        public int Compare(ChoTaskGroupCategory a, ChoTaskGroupCategory b)
        {
            return String.Compare(a.Name, b.Name);
        }
    }
    public class ChoTaskGroupCategory : INotifyPropertyChanged, ICloneable<ChoTaskGroupCategory>, IChoUndoable<ChoTaskGroupCategory>, IComparable<ChoTaskGroupCategory>
    {
        private readonly ObservableCollection<ChoTaskGroupCategory> _tasks = new SortedObservableCollection<ChoTaskGroupCategory>();
        private string _name = String.Empty;
        private string _description = String.Empty;
        private ChoTaskGroupCategory _parent;
        public bool IsDirty;
        public int _id;
        private int? _noOfRetry;
        private int? _sleepBetweenRetry;
        private int? _timeout;
        private string _machineName;
        private int _taskStatus;
        private string _cmdParameters;
        private string _workflowName;
        private string _scheduleName;

        public event PropertyChangedEventHandler PropertyChanged;

        public ChoTaskGroupCategory(int id, string name, string scheduleName, ChoTaskGroupCategory parent = null)
        {
            _id = id;
            this._name = name;
            _parent = parent;
            ParentGroupId = _parent != null ? _parent.Id : 0;
            _scheduleName = scheduleName;
        }

        public int Id
        {
            get { return _id; }
        }

        public int BatchId
        {
            get;
            set;
        }

        public string GroupName
        {
            get;
            internal set;
        }

        public string Name
        {
            get { return _name; }
            set 
            {
                if (_name == value) return;

                _name = value; 
                RaisePropertyChangedEvent("Name"); 
            }
        }

        public string WorkflowName
        {
            get { return _workflowName; }
            set
            {
                if (_workflowName == value) return;

                _workflowName = value;
                RaisePropertyChangedEvent("WorkflowName");
            }
        }

        public string ScheduleName
        {
            get { return _scheduleName; }
            set { _scheduleName = value; }
        }

        public int TaskStatus
        {
            get { return _taskStatus; }
            set
            {
                if (_taskStatus == value) return;

                _taskStatus = value;
                RaisePropertyChangedEvent("TaskStatus");
            }
        }

        public int ParentGroupId
        {
            get;
            set;
        }

        public int ExitCode
        {
            get;
            set;
        }

        public ChoETLWorkflowStatus WorkflowStatus
        {
            get;
            set;
        }

        public string CmdParameters
        {
            get { return _cmdParameters; }
            set
            {
                if (_cmdParameters == value) return;

                _cmdParameters = value;
                RaisePropertyChangedEvent("CmdParameters");
            }
        }

        public string BatchCmdParameters
        {
            get;
            set;
        }

        public int? MaxNoOfRetry
        {
            get { return _noOfRetry; }
            set
            {
                if (_noOfRetry == value) return;

                _noOfRetry = value;
                RaisePropertyChangedEvent("NoOfRetry");
            }
        }

        public int? SleepBetweenRetry
        {
            get { return _sleepBetweenRetry; }
            set
            {
                if (_sleepBetweenRetry == value) return;

                _sleepBetweenRetry = value;
                RaisePropertyChangedEvent("SleepBetweenRetry");
            }
        }

        public int? Timeout
        {
            get { return _timeout; }
            set
            {
                if (_timeout == value) return;

                _timeout = value;
                RaisePropertyChangedEvent("Timeout");
            }
        }

        public string Description
        {
            get { return _description; }
            set 
            {
                if (_description == value) return;

                _description = value; 
                RaisePropertyChangedEvent("Description"); 
            }
        }

        public string MachineName
        {
            get { return _machineName; }
            set
            {
                if (_machineName == value) return;

                _machineName = value;
                RaisePropertyChangedEvent("MachineName");
            }
        }

        public string BatchMachineName
        {
            get;
            set;
        }

        public string CreatedBy
        {
            get;
            set;
        }

        public DateTime CreatedDate
        {
            get;
            set;
        }

        public string ModifiedBy 
        { 
            get; 
            set; 
        }

        public DateTime ModifiedDate
        {
            get;
            set;
        }

        public string BatchModifiedBy
        {
            get;
            set;
        }

        public string AuditInfo
        {
            get 
            {
                if (Parent == null)
                    return null;
                else
                    return "Created By: {1}{0}Created Date: {2}{0}Modified By: {3}{0}Modified Date: {4}".FormatString(
                        Environment.NewLine, CreatedBy, CreatedDate, ModifiedBy, ModifiedDate); 
            }
        }

        public ObservableCollection<ChoTaskGroupCategory> Tasks
        {
            get { return _tasks; }
        }

        public override string ToString()
        {
            return "Task Group: " + Name;
        }

        public ChoTaskGroupCategory Parent { get { return _parent; } internal set { _parent = value; } }

        public bool IsTaskGroup
        {
            get;
            set;
        }

        public bool IsRoot
        {
            get { return Parent == null; }
        }

        private void RaisePropertyChangedEvent(string propertyName)
        {
            IsDirty = true; 
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public ChoTaskGroupCategory Clone()
        {
            ChoTaskGroupCategory cat = new ChoTaskGroupCategory(_id, _name, _scheduleName, _parent);
            cat._description = _description;
            cat._noOfRetry = _noOfRetry;
            cat._sleepBetweenRetry = _sleepBetweenRetry;
            cat._timeout = _timeout;
            cat._machineName = _machineName;
            cat._taskStatus = _taskStatus;
            cat._cmdParameters = _cmdParameters;
            cat._workflowName = _workflowName;
            cat.CreatedBy = CreatedBy;
            cat.CreatedDate = ModifiedDate;
            cat.ModifiedBy = ModifiedBy;
            cat.ModifiedDate = ModifiedDate;
            cat.BatchModifiedBy = BatchModifiedBy;

            return cat;
        }

        object ICloneable.Clone()
        {
            return Clone();
        }

        public ChoTaskGroupCategory Capture()
        {
            return Clone();
        }

        public void Undo(ChoTaskGroupCategory source)
        {
            if (source == null) return;

            _description = source._description;
            _noOfRetry = source._noOfRetry;
            _sleepBetweenRetry = source._sleepBetweenRetry;
            _timeout = source._timeout;
            _machineName = source._machineName;
            _taskStatus = source._taskStatus;
            _cmdParameters = source._cmdParameters;
            _workflowName = source._workflowName;
            CreatedBy = source.CreatedBy;
            CreatedDate = source.CreatedDate;
            ModifiedBy = source.ModifiedBy;
            ModifiedDate = source.ModifiedDate;
            BatchModifiedBy = source.BatchModifiedBy;
        }

        public override int GetHashCode()
        {
            return "{0}_{1}".FormatString(GroupName, Name).GetHashCode();
        }

        //public int CompareTo(object obj)
        //{
        //    ChoTaskGroupCategory cat = obj as ChoTaskGroupCategory;
        //    if (cat == null)
        //        throw new ArgumentException("Object is not ChoTaskGroupCategory");
        //    return this.Name.CompareTo(cat.Name);
        //}

        public int CompareTo(ChoTaskGroupCategory other)
        {
            return this.Name.CompareTo(other.Name);
        }
    }

    public class SortedObservableCollection<T>
        : ObservableCollection<T> where T : IComparable<T>
    {
        protected override void InsertItem(int index, T item)
        {
            //if (this.Count == 0)
            //{
            //    base.InsertItem(0, item);
            //    return;
            //}

            index = Compare(item, 0, this.Count - 1);

            base.InsertItem(index, item);
        }

        private int Compare(T item, int lowIndex, int highIndex)
        {
            int compareIndex = (lowIndex + highIndex) / 2;

            if (compareIndex == 0)
            {
                return SearchIndexByIteration(lowIndex, highIndex, item);
            }

            int result = item.CompareTo(this[compareIndex]);

            if (result < 0)
            {   //item precedes indexed obj in the sort order

                if ((lowIndex + compareIndex) < 100 || compareIndex == (lowIndex + compareIndex) / 2)
                {
                    return SearchIndexByIteration(lowIndex, compareIndex, item);
                }

                return Compare(item, lowIndex, compareIndex);
            }

            if (result > 0)
            {   //item follows indexed obj in the sort order

                if ((compareIndex + highIndex) < 100 || compareIndex == (compareIndex + highIndex) / 2)
                {
                    return SearchIndexByIteration(compareIndex, highIndex, item);
                }

                return Compare(item, compareIndex, highIndex);
            }

            return compareIndex;
        }

        /// <summary>
        /// Iterates through sequence of the collection from low to high index
        /// and returns the index where to insert the new item
        /// </summary>
        private int SearchIndexByIteration(int lowIndex, int highIndex, T item)
        {
            for (int i = lowIndex; i <= highIndex; i++)
            {
                if (item.CompareTo(this[i]) < 0)
                {
                    return i;
                }
            }
            return this.Count;
        }

        /// <summary>
        /// Adds the item to collection by ignoring the index
        /// </summary>
        protected override void SetItem(int index, T item)
        {
            this.InsertItem(index, item);
        }

        private const string _InsertErrorMessage
           = "Inserting and moving an item using an explicit index are not support by sorted observable collection";

        /// <summary>
        /// Throws an error because inserting an item using an explicit index
        /// is not support by sorted observable collection
        /// </summary>
        [Obsolete(_InsertErrorMessage)]
        public new void Insert(int index, T item)
        {
            throw new NotSupportedException(_InsertErrorMessage);
        }

        /// <summary>
        /// Throws an error because moving an item using explicit indexes
        /// is not support by sorted observable collection
        /// </summary>
        [Obsolete(_InsertErrorMessage)]
        public new void Move(int oldIndex, int newIndex)
        {
            throw new NotSupportedException(_InsertErrorMessage);
        }
    }
}
